#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
CodeBuddy OpenID Connect登录流程脚本
处理OpenID Connect授权流程
"""

import requests
from urllib.parse import unquote, parse_qs, urlparse
import re
from bs4 import BeautifulSoup
import json

class OpenIDLoginFlow:
    def __init__(self):
        self.session = requests.Session()
        self.setup_headers()
        
    def setup_headers(self):
        """设置浏览器请求头"""
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Accept-Encoding": "gzip, deflate, br",
            "DNT": "1",
            "Connection": "keep-alive",
            "Upgrade-Insecure-Requests": "1",
            "sec-ch-ua": '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": '"Windows"'
        })
    
    def get_openid_auth_page(self):
        """获取OpenID Connect授权页面"""
        print("🔐 获取OpenID Connect授权页面...")
        
        try:
            # 访问控制台页面，触发OpenID Connect流程
            console_url = "https://www.codebuddy.ai/console/accounts"
            response = self.session.get(console_url, allow_redirects=True)
            
            print(f"📊 状态码: {response.status_code}")
            print(f"🔗 最终URL: {response.url}")
            
            if "openid-connect/auth" in response.url:
                print("✅ 重定向到OpenID Connect授权页面")
                
                # 解析URL参数
                parsed_url = urlparse(response.url)
                params = parse_qs(parsed_url.query)
                
                print("📋 OpenID Connect参数:")
                for key, value in params.items():
                    print(f"  - {key}: {value[0]}")
                
                return response, params
            else:
                print("❌ 未重定向到OpenID Connect页面")
                return None, None
                
        except Exception as e:
            print(f"❌ 获取OpenID Connect页面失败: {e}")
            return None, None
    
    def follow_auth_redirect(self, auth_response):
        """跟随认证重定向到登录页面"""
        print("\n🔄 跟随认证重定向...")
        
        try:
            # 检查是否需要进一步重定向
            if auth_response.status_code == 200:
                # 检查页面内容是否包含自动重定向
                soup = BeautifulSoup(auth_response.text, 'html.parser')
                
                # 查找meta refresh重定向
                meta_refresh = soup.find('meta', {'http-equiv': 'refresh'})
                if meta_refresh:
                    content = meta_refresh.get('content', '')
                    if 'url=' in content:
                        redirect_url = content.split('url=')[1]
                        print(f"📍 发现meta refresh重定向: {redirect_url}")
                        
                        response = self.session.get(redirect_url, allow_redirects=True)
                        print(f"📊 重定向后状态码: {response.status_code}")
                        print(f"🔗 重定向后URL: {response.url}")
                        
                        return response
                
                # 查找JavaScript重定向
                scripts = soup.find_all('script')
                for script in scripts:
                    if script.string and 'location' in script.string:
                        print("📍 发现JavaScript重定向")
                        # 这里可能需要解析JavaScript来获取重定向URL
                
                # 查找登录表单
                login_form = soup.find('form')
                if login_form:
                    print("📝 在当前页面找到登录表单")
                    return auth_response
                
                # 如果没有找到重定向或表单，尝试手动构建登录URL
                if "openid-connect/auth" in auth_response.url:
                    # 从OpenID Connect URL构建登录URL
                    base_url = auth_response.url.split('/protocol/')[0]
                    login_url = f"{base_url}/protocol/openid-connect/auth"
                    
                    # 添加必要的参数来触发登录页面
                    parsed_url = urlparse(auth_response.url)
                    params = parse_qs(parsed_url.query)
                    
                    # 构建新的登录URL
                    new_params = {
                        'client_id': params.get('client_id', ['console'])[0],
                        'redirect_uri': params.get('redirect_uri', [''])[0],
                        'response_type': params.get('response_type', ['code'])[0],
                        'scope': params.get('scope', ['openid'])[0],
                        'state': params.get('state', [''])[0]
                    }
                    
                    # 尝试访问登录页面
                    login_response = self.session.get(login_url, params=new_params, allow_redirects=True)
                    print(f"📊 登录页面状态码: {login_response.status_code}")
                    print(f"🔗 登录页面URL: {login_response.url}")
                    
                    return login_response
            
            return auth_response
            
        except Exception as e:
            print(f"❌ 跟随重定向失败: {e}")
            return auth_response
    
    def find_login_form(self, response):
        """查找登录表单"""
        print("\n🔍 查找登录表单...")
        
        try:
            soup = BeautifulSoup(response.text, 'html.parser')
            
            # 查找登录表单
            login_forms = []
            
            # 方法1: 通过ID查找
            form_by_id = soup.find('form', {'id': re.compile(r'login|kc-form', re.I)})
            if form_by_id:
                login_forms.append(form_by_id)
            
            # 方法2: 通过class查找
            form_by_class = soup.find('form', {'class': re.compile(r'login|auth', re.I)})
            if form_by_class and form_by_class not in login_forms:
                login_forms.append(form_by_class)
            
            # 方法3: 查找包含用户名和密码字段的表单
            all_forms = soup.find_all('form')
            for form in all_forms:
                username_field = form.find('input', {'name': re.compile(r'username|email|user', re.I)})
                password_field = form.find('input', {'type': 'password'})
                
                if username_field and password_field and form not in login_forms:
                    login_forms.append(form)
            
            if login_forms:
                # 使用第一个找到的表单
                login_form = login_forms[0]
                print("✅ 找到登录表单")
                
                # 获取表单信息
                action = login_form.get('action', '')
                method = login_form.get('method', 'POST').upper()
                
                # 处理相对URL
                if action.startswith('/'):
                    base_url = f"{urlparse(response.url).scheme}://{urlparse(response.url).netloc}"
                    action = base_url + action
                elif not action.startswith('http'):
                    action = response.url
                
                # 获取表单字段
                form_fields = {}
                
                # 隐藏字段
                for hidden in login_form.find_all('input', {'type': 'hidden'}):
                    name = hidden.get('name')
                    value = hidden.get('value', '')
                    if name:
                        form_fields[name] = value
                        print(f"  隐藏字段: {name} = {value}")
                
                # 用户名字段
                username_field = login_form.find('input', {'name': re.compile(r'username|email|user', re.I)})
                password_field = login_form.find('input', {'type': 'password'})
                
                if username_field and password_field:
                    username_field_name = username_field.get('name')
                    password_field_name = password_field.get('name')
                    
                    print(f"  用户名字段: {username_field_name}")
                    print(f"  密码字段: {password_field_name}")
                    
                    return {
                        'action': action,
                        'method': method,
                        'username_field': username_field_name,
                        'password_field': password_field_name,
                        'hidden_fields': form_fields
                    }
                else:
                    print("❌ 表单中未找到用户名或密码字段")
            else:
                print("❌ 未找到登录表单")
                
                # 输出页面内容片段用于调试
                print("\n📄 页面内容片段:")
                text_content = soup.get_text()[:500]
                print(text_content + "...")
                
                # 检查是否已经登录
                if "logout" in text_content.lower() or "控制台" in text_content:
                    print("💡 可能已经登录")
                    return "already_logged_in"
            
            return None
            
        except Exception as e:
            print(f"❌ 查找登录表单失败: {e}")
            return None
    
    def perform_login(self, form_info):
        """执行登录"""
        print(f"\n🚀 执行登录...")
        
        if form_info == "already_logged_in":
            print("✅ 检测到已经登录")
            return True
        
        # 用户凭据
        username = "154hdgrkk7@dpmurt.my"
        password = "5gHpr0V!"
        
        print(f"👤 用户名: {username}")
        print(f"🔑 密码: {password}")
        print(f"📝 登录URL: {form_info['action']}")
        
        # 准备登录数据
        login_data = form_info['hidden_fields'].copy()
        login_data[form_info['username_field']] = username
        login_data[form_info['password_field']] = password
        
        # 添加可能需要的额外字段
        if 'credentialId' not in login_data:
            login_data['credentialId'] = ''
        
        print(f"📋 提交字段: {list(login_data.keys())}")
        
        # 设置请求头
        headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "Origin": f"{urlparse(form_info['action']).scheme}://{urlparse(form_info['action']).netloc}",
            "Referer": form_info['action'],
            "Cache-Control": "max-age=0",
            "Sec-Fetch-Dest": "document",
            "Sec-Fetch-Mode": "navigate",
            "Sec-Fetch-Site": "same-origin",
            "Sec-Fetch-User": "?1"
        }
        
        try:
            # 发送登录请求
            if form_info['method'] == 'POST':
                response = self.session.post(
                    form_info['action'],
                    data=login_data,
                    headers=headers,
                    allow_redirects=True,
                    timeout=30
                )
            else:
                response = self.session.get(
                    form_info['action'],
                    params=login_data,
                    headers=headers,
                    allow_redirects=True,
                    timeout=30
                )
            
            print(f"📊 登录响应状态码: {response.status_code}")
            print(f"🔗 登录后URL: {response.url}")
            
            return self.analyze_login_result(response)
            
        except Exception as e:
            print(f"❌ 登录请求失败: {e}")
            return False
    
    def analyze_login_result(self, response):
        """分析登录结果"""
        print("\n🔍 分析登录结果...")
        
        final_url = response.url.lower()
        response_text = response.text.lower()
        
        # 成功指标
        success_indicators = []
        failure_indicators = []
        
        # URL分析
        if "console" in final_url:
            success_indicators.append("URL包含console")
        if "account" in final_url:
            success_indicators.append("URL包含account")
        if "redirect" in final_url and "code=" in response.url:
            success_indicators.append("OAuth重定向成功")
        if "error" in final_url:
            failure_indicators.append("URL包含error")
        if "login" in final_url and "action" in final_url:
            failure_indicators.append("仍在登录页面")
        
        # 内容分析
        if "logout" in response_text:
            success_indicators.append("页面包含logout")
        if "dashboard" in response_text or "控制台" in response_text:
            success_indicators.append("页面包含控制台内容")
        if "invalid" in response_text or "incorrect" in response_text:
            failure_indicators.append("包含错误信息")
        
        # HTTP状态码
        if response.status_code == 200:
            success_indicators.append("HTTP 200状态码")
        elif response.status_code >= 400:
            failure_indicators.append(f"HTTP {response.status_code}错误")
        
        print(f"✅ 成功指标 ({len(success_indicators)}):")
        for indicator in success_indicators:
            print(f"  - {indicator}")
        
        print(f"❌ 失败指标 ({len(failure_indicators)}):")
        for indicator in failure_indicators:
            print(f"  - {indicator}")
        
        # 显示cookies
        self.display_cookies()
        
        # 最终判断
        if len(success_indicators) > len(failure_indicators) and response.status_code == 200:
            print("\n🎉 登录成功！")
            return True
        elif len(failure_indicators) > 0:
            print("\n❌ 登录失败")
            return False
        else:
            print("\n⚠️ 登录状态不确定")
            return None
    
    def display_cookies(self):
        """显示cookies信息"""
        print(f"\n🍪 Cookies信息:")
        if self.session.cookies:
            print(f"  获取到 {len(self.session.cookies)} 个cookies:")
            for cookie in self.session.cookies:
                print(f"    - {cookie.name}: {cookie.value[:30]}...")
        else:
            print("  未获取到cookies")
    
    def test_final_access(self):
        """测试最终访问"""
        print("\n🔐 测试最终访问...")
        
        test_urls = [
            "https://www.codebuddy.ai/console",
            "https://www.codebuddy.ai/console/accounts"
        ]
        
        success_count = 0
        
        for url in test_urls:
            try:
                response = self.session.get(url, allow_redirects=True, timeout=10)
                print(f"📊 {url}")
                print(f"  状态码: {response.status_code}")
                print(f"  最终URL: {response.url}")
                
                if response.status_code == 200 and "login" not in response.url.lower():
                    print(f"  ✅ 可以访问")
                    success_count += 1
                else:
                    print(f"  ❌ 无法访问")
                    
            except Exception as e:
                print(f"  ❌ 测试失败: {e}")
        
        return success_count > 0
    
    def run_openid_login_flow(self):
        """运行OpenID Connect登录流程"""
        print("=" * 60)
        print("🎯 CodeBuddy OpenID Connect登录流程")
        print("=" * 60)
        
        # 步骤1: 获取OpenID Connect授权页面
        auth_response, auth_params = self.get_openid_auth_page()
        if not auth_response:
            print("❌ 无法获取OpenID Connect页面")
            return False
        
        # 步骤2: 跟随重定向到登录页面
        login_response = self.follow_auth_redirect(auth_response)
        
        # 步骤3: 查找登录表单
        form_info = self.find_login_form(login_response)
        if not form_info:
            print("❌ 无法找到登录表单")
            return False
        
        # 步骤4: 执行登录
        login_success = self.perform_login(form_info)
        
        # 步骤5: 测试最终访问
        if login_success:
            final_success = self.test_final_access()
            return final_success
        
        return False

def main():
    """主函数"""
    print("🚀 开始CodeBuddy OpenID Connect登录测试...")
    
    login_flow = OpenIDLoginFlow()
    success = login_flow.run_openid_login_flow()
    
    print("\n" + "=" * 60)
    print("📋 OpenID Connect登录测试结果")
    print("=" * 60)
    
    if success:
        print("✅ OpenID Connect登录流程成功！")
        print("🎯 结论:")
        print("  1. 成功处理OpenID Connect授权流程")
        print("  2. 用户凭据验证通过")
        print("  3. 可以访问CodeBuddy控制台")
        print("\n💡 关键发现:")
        print("  - CodeBuddy使用标准的OpenID Connect流程")
        print("  - 需要处理多步重定向")
        print("  - 登录成功后会获得访问令牌")
    else:
        print("❌ OpenID Connect登录流程失败！")
        print("🎯 可能原因:")
        print("  1. 用户凭据无效")
        print("  2. OpenID Connect配置变更")
        print("  3. 需要额外的认证步骤")
        print("  4. 网站反自动化保护")

if __name__ == "__main__":
    main()